// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
import androidx.compose.material.MaterialTheme
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.ResourceLoader
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberTrayState
import kotlinx.coroutines.*
import java.io.Closeable
import java.io.File
import java.net.InetAddress

@Composable
@Preview
fun App() {
    val scope = rememberCoroutineScope()
    MaterialTheme {
        Column(modifier = Modifier.padding(16.dp)) {
            Row( verticalAlignment = Alignment.CenterVertically) {
                Button(onClick = {
                    scanner()
                }) {
                    Text("刷新")
                }
                Text("需要安卓版本5.0以上，并且已经打开USB调试 当前IP:${InetAddress.getLocalHost().hostAddress}")
            }

            Row ( verticalAlignment = Alignment.CenterVertically){
                val ipInput = remember { mutableStateOf("110.90.6.17") }
                TextField(ipInput.value, onValueChange = {
                    ipInput.value = it
                })
                Button(onClick = {
                    val ip = ipInput.value
                    if (ip.isIpv4()){
                        scope.launch {
                            binding(Device("${ip}:5555",ip,ip))
                        }
                    }
                }) {
                    Text("添加")
                }
            }


            LazyColumn {
                items(Running.devices){
                    Row (modifier = Modifier, verticalAlignment = Alignment.CenterVertically){
                        Text("${it.name}(${it.ip})")
                        Button(onClick = {
                            scope.launch {
                                if (it.isStarted.value){
                                    it.close()
                                }else{
                                    start(it)
                                }
                            }
                        }) {
                            if (it.isStarted.value){
                                Text("关闭")
                            }else{
                                Text("启动")
                            }
                        }
                        Button(onClick = {
                            scope.launch {
                                if (it.isBinding.value){
                                    unbinding(it)
                                }else{
                                    binding(it)
                                }
                            }
                        }) {
                            if (it.isBinding.value){
                                Text("Wifi已绑定")
                            }else{
                                Text("Wifi绑定")
                            }
                        }
                        Button(onClick = {
                            scope.launch {
                                it.isHD.value = !it.isHD.value
                            }
                        }) {
                            if (it.isHD.value){
                                Text("高清")
                            }else{
                                Text("高速")
                            }
                        }
                    }
                }
            }
        }
    }
}
object Running{
//    val Path by lazy { System.getProperty("user.dir")+System.getProperty("file.separator")+"app" }
    val Path by lazy { "D:\\AndoridProject\\Compose\\AndroidControl\\src\\main\\resources" }
    val devices = mutableStateListOf<Device>()
    init {
        scanner()
    }
}

fun String.isIpv4():Boolean{
    val ips = split(".")
    if (ips.size==4){
        try {
            ips.forEach {
                if (it.toInt() !in 0..255){
                    return false
                }
            }
        }catch (e:Exception){

        }
    }else{
        return false
    }
    return true
}
fun main() = application {
    val showWindow = remember { mutableStateOf(true) }
    Tray(icon = painterResource("icon.png"), menu = {
        Item("退出") {
            Running.devices.forEach {
                it.close()
            }
            exitApplication()
        }
    }, onAction = {
        showWindow.value = true
    })
    if (showWindow.value) {

        Window(onCloseRequest = {
            showWindow.value = false
            println("关闭")

        }, title = "安卓控制器", icon = painterResource("icon.png")) {
            App()
        }
    }
}

fun scanner(){
    val devices = getAdbDevices()
    val newDevice = arrayListOf<Device>()

    devices.forEach {
        val ipLiked = Running.devices.getDeviceForIP(it.ip)
        val oldDevice = Running.devices.getDeviceForId(it.id)
        if (oldDevice!=null){
            it.isStarted = oldDevice.isStarted
            it.isBinding = oldDevice.isBinding
            it.isHD = oldDevice.isHD
            it.process = oldDevice.process
        }
        if (ipLiked==null||ipLiked.id==it.id){
            if (it.isBinding.value){
                for (addDevice in newDevice){
                    if (addDevice.id!=it.id&&addDevice.ip==it.ip&&addDevice.name==it.name){
                        newDevice.remove(addDevice)
                        break
                    }
                }
            }
            newDevice.add(it)

        }
    }
    Running.devices.clear()
    Running.devices.addAll(newDevice)
}

data class Device(var id:String, var name:String, val ip: String, var isBinding:MutableState<Boolean> = mutableStateOf(id.startsWith(ip)),var isHD:MutableState<Boolean> = mutableStateOf(true), var isStarted:MutableState<Boolean> = mutableStateOf(false), var process: Process? = null):Closeable {
    override fun close() {
        process?.toHandle()?.children()?.forEach(ProcessHandle::destroy)
    }
}

fun SnapshotStateList<Device>.getDeviceForId(id:String):Device?{
    forEach {
        if (it.id==id){
            return it
        }
    }
    return null
}

fun SnapshotStateList<Device>.getDeviceForIP(ip:String):Device?{
    forEach {
        if (it.id==ip){
            return it
        }
    }
    return null
}

suspend fun start(device: Device){
    withContext(Dispatchers.IO){
        val cmd= if (device.isHD.value){
            "cmd /c cd /d ${Running.Path} && scrcpy -b16M --turn-screen-off --stay-awake --no-audio --video-codec=h265 -s ${device.id}"
        }else{
            "cmd /c cd /d ${Running.Path} && scrcpy -b1M -m800 --turn-screen-off --stay-awake --no-audio --video-codec=h265 -s ${device.id}"
        }
        device.isStarted.value = true
        val run = Runtime.getRuntime()
        try {
            val process = run.exec(cmd)
            device.process = process
            process.waitFor()
        } catch (e:Exception) {
            e.printStackTrace()
        }
        device.isStarted.value = false
    }
}

suspend fun binding(device: Device){
    withContext(Dispatchers.IO){
        val cmd="cmd /c cd /d ${Running.Path} && adb -s ${device.id} tcpip 5555"
        val cmdLink="cmd /c cd /d ${Running.Path} && adb connect ${device.ip}"

        val run = Runtime.getRuntime()
        try {
            if (!device.id.startsWith(device.ip)){
                val process = run.exec(cmd)
                process.waitFor()
            }
            val linkProcess = run.exec(cmdLink)
            linkProcess.waitFor()
            linkProcess.inputStream.use {
                val buf = ByteArray(8*1024)
                while (true){
                    val length = it.read(buf)
                    if (length>-1){
                        val info = String(buf,0,length)
                        if (info.contains("connected")){
                            device.id = device.ip+":5555"
                            device.isBinding.value = true
                            if (Running.devices.getDeviceForId(device.id)==null){
                                scanner()
                            }
                            break
                        }else{
                            println(info)
                        }
                    }else{
                        break
                    }
                }

            }

        } catch (e:Exception) {
            println(e.toString())
            e.printStackTrace()
        }

    }

}

suspend fun unbinding(device: Device){
    withContext(Dispatchers.IO){
        val cmd="cmd /c cd /d ${Running.Path} && adb -s ${device.id} kill-server"

        val run = Runtime.getRuntime()
        try {
            val process = run.exec(cmd)
            process.waitFor()
            Running.devices.remove(device)
            scanner()
        } catch (e:Exception) {
            e.printStackTrace()
        }

    }
}

fun getAdbDevices():List<Device>{
    val list = arrayListOf<Device>()

    val cmd="cmd /c cd /d ${Running.Path} && adb devices -l"
    val run = Runtime.getRuntime()
    try {
        val process = run.exec(cmd)
        process.waitFor()
        process.inputStream.use {
            val buf = ByteArray(8*1024)
            while (true){
                val length = it.read(buf)
                if (length>-1){
                    val info = String(buf,0,length)
                    info.split("\n").forEach { sub->
                        val names = sub.split("device product:")
                        if (names.size>=2&&names[1].contains("model")){
                            val id = names[0].trim()
                            list.add(Device(id,names.last().split("device").first().split("model:").last().trim(),getDeviceIP(id)))
                        }
                    }

                }else{
                    break
                }
            }

        }
    } catch (e:Exception) {
        e.printStackTrace();
    }
    return list
}

fun getDeviceIP(id:String):String{
    val ipv4 = id.split(":").first()
    if (ipv4.isIpv4()){
        return ipv4
    }
//    return "110.90.6.17"
    //windows
    val cmd="cmd /c cd /d ${Running.Path} && adb -s ${id} shell ip addr show wlan0"
    //macOs
//    val cmd="${Running.Path} adb -s ${id} shell getprop ro.product.model"
    //windows
    val run = Runtime.getRuntime()
    try {
        val process = run.exec(cmd)
        process.waitFor()
        process.inputStream.use {
            return String(it.readAllBytes()).split("inet ").last().split("/").first()
        }
    }catch (e:Exception){
        return ""
    }
}